package node_pieces

import (
	"encoding/binary"
	"errors"
	"fmt"
	"github.com/zeebo/errs"
	"strconv"
	"strings"
)

const pieceSize = 5

var Error = errs.Class("nodePieces")

type NodePieces map[uint8]uint32

func FromBytes(b []byte) (NodePieces, error) {
	l := len(b)
	if l%pieceSize != 0 {
		return nil, errors.New("FromBytes error")
	}
	m := make(NodePieces)
	n := l / pieceSize
	for i := 0; i < n; i++ {
		k := i * pieceSize
		m[b[k]] = binary.BigEndian.Uint32(b[k+1 : pieceSize*(i+1)])
	}
	return m, nil
}

// FromString s:
func FromString(s string) (NodePieces, error) {
	arr := strings.Split(s, ",")
	np := make(NodePieces)
	for i := range arr {
		_arr := strings.SplitN(arr[i], ":", 2)
		n, err := strconv.Atoi(_arr[0])
		if err != nil {
			return nil, err
		}
		idx, err := strconv.ParseInt(_arr[1], 10, 32)
		if err != nil {
			return nil, err
		}
		np[uint8(n)] = uint32(idx)
	}
	return np, nil
}

func (np NodePieces) MarshalJSON() ([]byte, error) {
	return []byte(`"` + np.String() + `"`), nil
}

func (np *NodePieces) UnmarshalJSON(b []byte) error {
	if len(b) <= 2 {
		return fmt.Errorf("bytes have wrong length %d ", len(b))
	}
	if b[0] != '"' && b[len(b)-1] != '"' {
		return fmt.Errorf("expected quotes around string")
	}
	var err error
	*np, err = FromString(string(b[1 : len(b)-1]))
	if err != nil {
		return Error.Wrap(err)
	}
	return nil
}

// Marshal serializes uuid.
func (np NodePieces) Marshal() ([]byte, error) {
	return np.Bytes(), nil
}

// MarshalTo serializes uuid into the passed byte slice.
func (np *NodePieces) MarshalTo(data []byte) (n int, err error) {
	n = copy(data, np.Bytes())
	return n, nil
}

// Unmarshal deserializes uuid.
func (np *NodePieces) Unmarshal(data []byte) error {
	var err error
	*np, err = FromBytes(data)
	return err
}

// Bytes 返回 字节序 的数据，不可与 String 通过类型互转
func (np NodePieces) Bytes() []byte {
	b := make([]byte, len(np)*pieceSize)
	i := 0
	for k, v := range np {
		offset := i * pieceSize
		copy(b[offset:], []byte{k})
		b[offset+1] = byte(v >> 24)
		b[offset+2] = byte(v >> 16)
		b[offset+3] = byte(v >> 8)
		b[offset+4] = byte(v)
		i++
	}
	return b
}

// String 返回 1:2,3:5 这样映射关系可见的字符串，不可与 Bytes 通过类型互转
func (np NodePieces) String() string {
	str := make([]string, len(np))
	i := 0
	for k, v := range np {
		str[i] = fmt.Sprintf("%d:%d", k, v)
		i++
	}
	return strings.Join(str, ",")
}

// Size 数量
func (np NodePieces) Size() int {
	return len(np)
}

// Length 落库字节长度
func (np NodePieces) Length() int {
	return len(np) * pieceSize
}

func (np NodePieces) Folded() bool {
	vs := make(map[uint32]byte)
	for _, v := range np {
		if _, ok := vs[v]; ok {
			return true
		}
		vs[v] = '1'
	}
	return false
}
